#-*-coding:utf-8-*-

from iomodule.read import read_profile_data
from iomodule.write import write_vtypes
import random
import ConfigParser
import optparse
from iomodule.read import read_routes_data
from iomodule.write import write_routes
from iomodule.write import write_sumo_cfg
from iomodule.read import read_network_length
import os
import math

def generate_vtypes(profile):
    assert type(profile) is dict
    vtypes = []
    for clazz in profile['class']:
        for group in clazz['groups']:
            for x in group['types']:
                vtype = {}
                for key in clazz:
                    if key != 'groups':
                        vtype.setdefault(key, clazz[key])
                for key in group:
                    if key != 'types':
                        vtype.setdefault(key, group[key])
                for key in x:
                    vtype.setdefault(key, x[key])
                vtypes.append(vtype)
    prefix = "passenger"
    for i in range(len(vtypes)):
        vtypes[i].setdefault("id", "%s%d" % (prefix, i + 1))
        length = random.randint(3, 6)
        vtypes[i].setdefault("length", length)
    return vtypes

def random_vtype(vtypes):
    vtype = {}
    while True:
        id = random.randint(0, len(vtypes) - 1)
        prob = random.random()
        accept = 1.0 * vtypes[id]['percent'] * vtypes[id]['proportion'] * vtypes[id]['population']
        if prob <= accept:
            # return vtypes[id]['id']
            vtype_id = id
            break
    # add some noise
    # random.gauss(0, 0.2) in range [-0.5, 0.5]
    vtype.setdefault("accel",  vtypes[vtype_id]['accel'] + round(random.gauss(0, 0.15),2))
    vtype.setdefault('decel',  vtypes[vtype_id]['decel'] + round(random.gauss(0, 0.15),2))
    # random.gauss(0, 0.01) in range[-0.05, 0.05]
    vtype.setdefault('sigma', vtypes[vtype_id]['sigma'] + round(random.gauss(0, 0.01),2))

    vtype.setdefault('maxSpeed', vtypes[vtype_id]['maxSpeed'] + round(random.gauss(2, 1),0))
    vtype.setdefault('minGap', vtypes[vtype_id]['minGap'] + round(random.gauss(0, 0.1), 1))
    vtype.setdefault("length", vtypes[vtype_id]['length'])
    # random.gauss(0.2, 0.1) in range[-0.01, 0.3]
    while True:
        t = vtypes[vtype_id]['tau'] + round(random.gauss(0.2, 0.05), 1)
        if  t >= 1.0:
            vtype.setdefault('tau', t)
            break
    # random.gauss(0, 0.01) in range[-0.05, 0.05]
    vtype.setdefault('speedFactor',  vtypes[vtype_id]['speedFactor'] + round(random.gauss(0, 0.01), 2))
    vtype.setdefault('speedDev', 0.0)
    return vtype

def test_generate_vtype():
    profile = read_profile_data("../../resources/profile.xml")
    vtypes = generate_vtypes(profile)

    write_vtypes("../../data/simu/vtypes.add.xml", vtypes)

def test_write_routes():
    routes = read_routes_data("../../data/simu/325_s.rou.xml")
    write_routes("../../data/simu/test.rou.xml", routes)

def generate_depart(base, lower_bound, upper_bound):
    while True:
        depart = int(random.gauss(mu=1800, sigma=2000)) + base
        if lower_bound <= depart <= upper_bound:
            return depart

def set_depart(trips, begin, end):
    assert type(trips) is list
    routes = []

    for i, trip in enumerate(trips):
        assert type(trip) is dict
        # trip['depart'] = generate_depart(float(trip['depart']), begin, end)
        trip['depart'] = random.randrange(begin, end)
        routes.append(trip)
    return sorted(routes, key=lambda d: d["depart"], reverse=False)

def add_stop(trip, edges):
    route = trip['route'].strip().split(' ')
    e_id = random.randint(1, len(route) - 2)
    e_id = route[e_id]
    while edges[e_id] < 10:
        e_id = random.randint(2, len(route) - 2)
        e_id = route[e_id]

    end_pos = random.random() * (edges[e_id] - 10) + 10
    duration = random.randint(1, 30)
    trip.setdefault("stop", {"lane":"{0}_0".format(e_id), "endPos":end_pos, "duration":duration})
    return trip

def add_stops(trips, edges, stop):
    for i, trip in enumerate(trips):
        prob = random.random()
        if prob < 0.15:
            trips[i] = add_stop(trip, edges)
    return trips

def set_sumo_config(configure, froute):
    sumo_config = {}
    # define input
    input = {}
    input.setdefault("net-file", "hangzhou.net.xml")
    input.setdefault("route-files", froute)
    input.setdefault("additional-files", configure['vtypes'])
    sumo_config.setdefault("input", input)
    # define output
    prefix = froute.split('.')[0]
    print prefix
    output = {}
    output.setdefault("summary-output", "{0}.summary.xml".format(prefix))
    # output.setdefault("tripinfo-output", "{0}.tripinfo.xml".format(prefix))
    output.setdefault("fcd-output", "{0}.gps.xml".format(prefix))
    output.setdefault("fcd-output.geo", "true")
    sumo_config.setdefault("output", output)
    #define time
    time = {}
    time.setdefault("begin", configure['begin'])
    time.setdefault("end", configure['end'])
    time.setdefault("step-length", configure["step"])
    sumo_config.setdefault("time", time)
    #define processing
    processing = {}
    processing.setdefault("time-to-teleport", configure["teleport"])
    processing.setdefault("ignore-junction-blocker", configure["junction"])
    processing.setdefault("lanechange.allow-swap", configure["lanechange"])
    processing.setdefault("routing-algorithm", "dijkstra")
    sumo_config.setdefault("processing", processing)

    routing = {}
    routing.setdefault("device.rerouting.probability", 0.5)
    routing.setdefault("device.rerouting.period", 600)
    sumo_config.setdefault("routing", routing)
    report = {}
    report.setdefault("verbose", "true")
    report.setdefault("log", "{0}.logs".format(prefix))
    sumo_config.setdefault("report", report)
    return sumo_config

def parse_configuration(conf=None):
    parser = ConfigParser.RawConfigParser()
    parser.read(conf)
    configure = {}

    configure.setdefault("profile", parser.get("input", "profile"))
    configure.setdefault("route", parser.get("input", "route"))
    configure.setdefault("network", parser.get("input", "network"))
    configure.setdefault("start", parser.getint("input", "start"))
    configure.setdefault("finish", parser.getint("input", "finish"))

    configure.setdefault("begin", parser.getint("sumo", "begin"))
    configure.setdefault("end", parser.getint("sumo", "end"))
    configure.setdefault("step", parser.getint("sumo", "step"))
    configure.setdefault("teleport", parser.getint("sumo", "teleport"))
    configure.setdefault("junction", parser.getint("sumo", "junction"))
    configure.setdefault("lanechange", parser.get("sumo", "lanechange"))

    configure.setdefault("days", parser.getint("output", "days"))
    configure.setdefault("prefix", parser.get("output", "prefix"))
    configure.setdefault("outputdir", parser.get("output", "outputdir"))
    configure.setdefault("vtypes", parser.get("output", "vtypes"))
    configure.setdefault("baseroute", parser.get("output", "baseroute"))

    return configure


def dispatcher(configure):
    if not os.path.exists(configure['outputdir']) or os.path.isfile(configure['outputdir']):
        os.mkdir(configure['outputdir'])

    profile = read_profile_data(configure["profile"])
    vtypes = generate_vtypes(profile)
    # write_vtypes(os.path.join(configure['outputdir'], configure['vtypes']), vtypes)
    stops = {}
    for vtype in vtypes:
        stops.setdefault(vtype["id"], vtype["stop"])
    network = read_network_length(configure["network"])
    # load routes
    trips = read_routes_data(configure['route'])
    user_vtypes = []
    # first assign vtypes to trips
    for i in range(len(trips)):
        vtype = random_vtype(vtypes)
        trips[i].setdefault("type", trips[i]['id'])
        trips[i]["type"] = trips[i]['id']
        vtype.setdefault("id", trips[i]['id'])
        user_vtypes.append(vtype)

    write_vtypes(os.path.join(configure["outputdir"], configure['vtypes']), user_vtypes)
    print "begin to write baseroute file"
    # write base route
    write_routes(os.path.join(configure['outputdir'], configure["baseroute"]), trips)
    #
    # modify the depart time for different days
    for day in range(configure['days']):
        fname = os.path.join(configure['outputdir'], "{0}_{1}.rou.xml".format(configure['prefix'], day))
        print "generate {0}....".format(fname)
        routes = set_depart(trips, configure['begin'], configure['end'])
        print "write {0}....".format(fname)
        routes = add_stops(routes, network, stops)
        write_routes(fname, routes)
        sumo_config = set_sumo_config(configure, "{0}_{1}.rou.xml".format(configure["prefix"], day))
        if day >= configure['days'] / 2:
            sumo_config["output"].pop("fcd-output")
            sumo_config["output"].pop("fcd-output.geo")
        sumo_name = "{0}_{1}.sumo.cfg".format(configure['prefix'], day)
        write_sumo_cfg(os.path.join(configure['outputdir'], sumo_name), sumo_config)
    print "finished!"

def get_options(args=None):
    optparser = optparse.OptionParser()
    optparser.add_option("-c", "--config", dest="config", help="the configuration file")
    (options, args) = optparser.parse_args(args)
    if not options.config:
        optparser.print_help()
        exit(0)
    return options

def main(options=None):
    configure = parse_configuration(options.config)
    dispatcher(configure)

if __name__ == '__main__':
    # test_generate_vtype()
    # test_write_routes()
    main(get_options())